use anyhow::Error;
use anyhow::Result;

use crate::model::kline::Klt;
use crate::model::FudaThing;
use crate::model::trade::trade_order_data::TradeOrderData;
use chrono::DateTime;
use chrono::Utc;
use serde::Deserialize;
use serde::Serialize;
use serde::Serializer;
use surrealdb::sql::Thing;
use ts_rs::TS;

///
/// 交易指令，由trade_bot生成，可以根据 kline_id的 klts 标记在K线上,
///
/// 一个trade_bot在任一 kline_id 只可以生成最多一个trade_order
///
/// trade_order
///
/// [tbot_id](super::trade_bot_with),kline_id 决定唯一
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct TradeOrder {
    /// TradeOrder Id, 自动创建
    #[ts(as = "Option<FudaThing>")]
    pub id: Option<Thing>,

    ///
    /// trade_bot_id, the trade_order creator, primary key
    ///
    pub trade_bot_id: String,
    ///
    /// kline_id, Kline的id, primary key
    ///
    pub kline_id: String,

    /// 冗余字段, security_id, get by trade_bot.id, used for select
    pub security_id: Option<String>,
    /// 冗余字段, strategy_id, get by trade_bot.id, used for select
    pub strategry_id: Option<String>,
    /// 冗余字段, klt of strategy, get by kline_id, used for select
    pub klt: Option<Klt>,
    /// 冗余字段，get by kline_id
    pub klts: Option<DateTime<Utc>>,
    /// 冗余字段 OHLCV成交量，get by kline_id,
    pub kl_volume: Option<u64>,

    /// 交易指令数据
    #[serde(serialize_with = "serialize_data")]
    pub data: Option<TradeOrderData>,

    /// 记录更新日期 HLC
    pub last_updated: Option<String>,
}

impl TradeOrder {
    pub fn serialize(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string(self)
    }
}

fn serialize_data<S>(data: &Option<TradeOrderData>, serializer: S) -> Result<S::Ok, S::Error>
    where S: Serializer
{
    match data {
        Some(dt) => {
            let mut dtmap = serde_json::Map::new();
            dtmap.insert(
                "type".into(),
                serde_json::Value::String(
                    format!("com.fuda.trade.model.TradeOrderData.{}", dt.tname())
                )
            );
            match dt {
                TradeOrderData::Buy(data) => {
                    let data_map = serde_json
                        ::to_value(data)
                        .map_err(|e| serde::ser::Error::custom(e.to_string()))?;
                    dtmap.extend(data_map.as_object().unwrap().clone());
                }

                TradeOrderData::Sell(data) => {
                    let data_map = serde_json
                        ::to_value(data)
                        .map_err(|e| serde::ser::Error::custom(e.to_string()))?;
                    dtmap.extend(data_map.as_object().unwrap().clone());
                }
            }

            serializer.collect_map(dtmap.iter())
        }
        None => serializer.serialize_none(),
    }
}

/// A wrapper around Vec<TradeOrder> to implement Into<zenoh::value::Value>
#[derive(Debug, Serialize, Deserialize)]
pub struct TradeOrderList(pub Vec<TradeOrder>);

impl From<Vec<TradeOrder>> for TradeOrderList {
    fn from(vec: Vec<TradeOrder>) -> Self {
        TradeOrderList(vec)
    }
}

impl Into<zenoh::value::Value> for TradeOrderList {
    fn into(self) -> zenoh::value::Value {
        match serde_json::to_vec(&self.0) {
            Ok(bytes) => zenoh::value::Value::from(bytes),
            Err(e) => panic!("Failed to serialize TradeOrderList to JSON: {}", e),
        }
    }
}

impl TryFrom<&zenoh::value::Value> for TradeOrderList {
    type Error = Error;

    fn try_from(value: &zenoh::value::Value) -> Result<Self, Self::Error> {
        let payload = &value.payload;
        let mut bytes = Vec::new();

        for slice in payload.zslices() {
            bytes.extend_from_slice(slice);
        }

        let deserialized: Result<Vec<TradeOrder>, _> = serde_json::from_slice(&bytes);
        deserialized.map(TradeOrderList).map_err(Into::into)
    }
}

#[cfg(test)]
mod test {
    use crate::model::trade::trade_order_data::{ self, PricingType };

    use super::*;
    use log::debug;

    #[tokio::test]
    async fn test_trade_order_serde() {
        pretty_env_logger::init_timed();
        debug!("debug No more records available; exit the loop");
        println!("print No more records available; exit the loop");

        let strategy = TradeOrder {
            id: Some(Thing::from(("trade_order", "xxxx12412"))),
            trade_bot_id: "ajsasdjsjfjdjfas".to_string(),
            data: Some(
                TradeOrderData::Buy(trade_order_data::Buy {
                    security_code: "600519".to_string(),
                    security_name: "贵州茅台".to_string(),
                    pricing_type: PricingType::Limit,
                    buy_price: 100.0,
                    volume: 100,
                    desc: "short 100 shares".to_string(),
                    is_auto: false,
                    spl_price: 99.0,
                })
            ),
            ..Default::default()
        };

        let payload = serde_json::to_string(&strategy).unwrap();

        println!("payload: {}", payload)
    }
}
